package com.wngbms.feigndemo.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.async.DeferredResult;
import org.springframework.web.context.request.async.WebAsyncTask;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;

@Slf4j
@RestController
public class AsyncController {

    @GetMapping("/asyncGet")
    public DeferredResult<String> asyncGet()
            throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        DeferredResult<String> result = new DeferredResult<>();
        log.info("asyncGet start");
        new Thread(() ->{
            try {
                log.info("begin handle");
                TimeUnit.SECONDS.sleep(10);
                log.info("end handle");

                result.setResult("handle success");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
        log.info("asyncGet end");

        /*List<Class<?>> classList = loadClass();
        for (Class<?> type : classList) {

            try {
                Method $java = type.getDeclaredMethod("$jacocoInit");
                Object invoke = $java.invoke(type);

                boolean[] b = (boolean[]) invoke;
                Arrays.fill(b, true);
            } catch (Exception e) {
                log.error("exp", e);
            }

            System.out.println("============finish yyds===================");
        }*/

        Class<? extends AsyncController> type = this.getClass();
        Method $java = type.getDeclaredMethod("$jacocoInit");
        Object invoke = $java.invoke(type);

        boolean[] b = (boolean[]) invoke;
        Arrays.fill(b, true);

        return result;
    }

    @GetMapping("/asyncGet2")
    public WebAsyncTask<String> asyncGet2() {
        log.info("asyncGet2 start");
        Callable<String> callable = new Callable<String>() {
            @Override
            public String call() throws Exception {
                log.info("begin handle");
                TimeUnit.SECONDS.sleep(10);
                log.info("end handle");
                return "handle success";
            }
        };
        log.info("asyncGet2 end");
        return new WebAsyncTask<>(callable);
    }

    @GetMapping("/asyncGet3")
    public WebAsyncTask<String> asyncGet3() {
        log.info("asyncGet3 start");
        Callable<String> callable = new Callable<String>() {
            @Override
            public String call() throws Exception {
                log.info("begin handle");
                TimeUnit.SECONDS.sleep(10);
                log.info("end handle");
                return "handle success";
            }
        };

        log.info("asyncGet3 end");
        WebAsyncTask<String> task = new WebAsyncTask<>(15000, callable);
        task.onCompletion(() -> {
            /**
             * 此方法的执行线程是http-nio线程, 但不是请求过来的线程.
             * 2022-03-25 21:28:19.335 [http-nio-8002-exec-7] [INFO ] [] com.wngbms.feigndemo.controller.AsyncController.asyncGet3[53] - asyncGet3 start
             * 2022-03-25 21:28:19.336 [http-nio-8002-exec-7] [INFO ] [] com.wngbms.feigndemo.controller.AsyncController.asyncGet3[64] - asyncGet3 end
             * 2022-03-25 21:28:19.339 [mvc-async-thread9] [INFO ] [] com.wngbms.feigndemo.controller.AsyncController.call[57] - begin handle
             * 2022-03-25 21:28:29.352 [mvc-async-thread9] [INFO ] [] com.wngbms.feigndemo.controller.AsyncController.call[59] - end handle
             * 2022-03-25 21:28:29.358 [http-nio-8002-exec-8] [INFO ] [] com.wngbms.feigndemo.controller.AsyncController.lambda$asyncGet3$1[67] - on completion
             */
            log.info("on completion");
        });

        task.onTimeout(() -> {
            log.warn("on timeout");
            return "timeout";
        });
        return task;
    }

    @GetMapping("/asyncGet4")
    public Callable<String> asyncGet4() {
        log.info("4 start");

        Callable<String> callable = new Callable<String>() {
            @Override
            public String call() throws Exception {
                log.info("begin handle");
                TimeUnit.SECONDS.sleep(10);
                log.info("end handle");
                return "handle success";
            }
        };

        log.info("4 end");
        return callable;
    }

    @GetMapping("/syncGet")
    public String syncGet() {
        log.info("syncGet start");
        try {
            TimeUnit.SECONDS.sleep(60);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.info("syncGet end");
        return "handle success";
    }

    public static List<Class<?>> loadClass() throws IOException, ClassNotFoundException {
        ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
        Enumeration<URL> resources = contextClassLoader.getResources("../classes/com");
        URL url = resources.nextElement();
        String filePath = url.getFile();
        List<File> fileList = new LinkedList<>();
        loadClass(new File(filePath), fileList);
        List<Class<?>> typeList = new ArrayList<>(fileList.size());
        for (File file : fileList) {
            String classes = file.getAbsolutePath()
                    .split("classes.")[1]
                    .replace("\\", ".")
                    .replace(".class", "");
            System.out.println(classes);
            typeList.add(Class.forName(classes));
        }
        return typeList;
    }

    public static void loadClass(File file, List<File> fileList) {
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (File f : files) {
                if (f.isDirectory()) {
                    loadClass(f, fileList);
                } else {
                    fileList.add(f);
                }
            }
        } else {
            fileList.add(file);
        }
    }


}
